﻿/*****************************************************************************************
   
   Copyright (c) Microsoft Corporation. All rights reserved.

   Use of this code sample is subject to the terms of the Microsoft
   Public License, a copy of which should always be distributed with
   this file.  You can also access a copy of this license agreement at:
   http://www.microsoft.com/opensource/licenses.mspx#Ms-PL

 ****************************************************************************************/
using System;

namespace Microsoft.CSA.FeedSync.FeedSyncLibrary
{
    public class FeedSyncFeed<T> where T : Microsoft.CSA.FeedSync.FeedSyncLibrary.FeedSyncItem, new()
    {
        protected System.Xml.XmlDocument m_XmlDocument;

        protected Microsoft.CSA.FeedSync.FeedSyncLibrary.Sharing m_Sharing;

        protected System.Collections.Generic.SortedList<string, T> m_FeedSyncItemSortedList = new System.Collections.Generic.SortedList<string, T>();

        protected System.Xml.XmlNamespaceManager m_XmlNamespaceManager;

        protected System.Xml.XmlElement m_ItemContainerXmlElement = null;

        internal FeedSyncFeed()
        {
        }

        public FeedSyncFeed(string i_FeedContents, System.Xml.XmlQualifiedName i_ItemContainerXmlQualifiedName)
        {
            if (System.String.IsNullOrEmpty(i_FeedContents))
                throw new System.ArgumentException("Invalid value for i_FeedContents!");

            System.Xml.XmlDocument XmlDocument = new System.Xml.XmlDocument();
            XmlDocument.LoadXml(i_FeedContents);

            System.Xml.XmlNamespaceManager XmlNamespaceManager = new System.Xml.XmlNamespaceManager(XmlDocument.NameTable);

            this.Initialize
                (
                XmlDocument,
                XmlNamespaceManager,
                i_ItemContainerXmlQualifiedName
                );
        }

        public FeedSyncFeed(System.Xml.XmlDocument i_XmlDocument, System.Xml.XmlQualifiedName i_ItemContainerXmlQualifiedName)
        {
            if (i_XmlDocument == null)
                throw new System.ArgumentException("i_XmlDocument must be non-null!");

            System.Xml.XmlNamespaceManager XmlNamespaceManager = new System.Xml.XmlNamespaceManager(i_XmlDocument.NameTable);

            this.Initialize
                (
                i_XmlDocument,
                XmlNamespaceManager,
                i_ItemContainerXmlQualifiedName
                );
        }

        public FeedSyncFeed(System.Xml.XmlDocument i_XmlDocument, System.Xml.XmlNamespaceManager i_XmlNamespaceManager, System.Xml.XmlQualifiedName i_ItemContainerXmlQualifiedName)
        {
            this.Initialize
                (
                i_XmlDocument,
                i_XmlNamespaceManager,
                i_ItemContainerXmlQualifiedName
                );
        }

        protected void Initialize(System.Xml.XmlDocument i_XmlDocument, System.Xml.XmlNamespaceManager i_XmlNamespaceManager, System.Xml.XmlQualifiedName i_ItemContainerXmlQualifiedName)
        {
            if (i_XmlDocument == null)
                throw new System.ArgumentException("i_XmlDocument must be non-null!");

            if (i_XmlNamespaceManager == null)
                throw new System.ArgumentException("i_XmlNamespaceManager must be non-null!");

            if (i_ItemContainerXmlQualifiedName == null)
                throw new System.ArgumentException("i_ItemContainerXmlQualifiedName must be non-null!");

            m_XmlDocument = i_XmlDocument;
            
            m_XmlNamespaceManager = i_XmlNamespaceManager;
            if (i_XmlNamespaceManager == null)
                m_XmlNamespaceManager = new System.Xml.XmlNamespaceManager(m_XmlDocument.NameTable);

            string FeedSyncNamespacePrefix = Microsoft.CSA.FeedSync.FeedSyncLibrary.XmlNamespaceManagerHelper.InitializeXmlNamespaceManager
                (
                m_XmlDocument, 
                m_XmlNamespaceManager,
                Microsoft.CSA.FeedSync.FeedSyncLibrary.Constants.FEEDSYNC_XML_NAMESPACE_PREFIX,
                Microsoft.CSA.FeedSync.FeedSyncLibrary.Constants.FEEDSYNC_XML_NAMESPACE_URI
                );

            string ItemContainerNamespacePrefix = "icnp" + System.Guid.NewGuid().ToString();
            m_XmlNamespaceManager.AddNamespace
                (
                ItemContainerNamespacePrefix,
                i_ItemContainerXmlQualifiedName.Namespace
                );

            m_ItemContainerXmlElement = (System.Xml.XmlElement)m_XmlDocument.DocumentElement.SelectSingleNode
                (
                ItemContainerNamespacePrefix + ":" + i_ItemContainerXmlQualifiedName.Name, 
                m_XmlNamespaceManager
                );

            if (m_ItemContainerXmlElement == null)
                throw new System.Exception("No item container element found!");

            //  Get reference to 'sx:sharing' element
            string XPathQuery = System.String.Format
                (
                "{0}:{1}",
                FeedSyncNamespacePrefix,
                Microsoft.CSA.FeedSync.FeedSyncLibrary.Constants.SHARING_ELEMENT_NAME
                );

            System.Xml.XmlElement SharingXmlElement = (System.Xml.XmlElement)m_ItemContainerXmlElement.SelectSingleNode
                (
                XPathQuery,
                m_XmlNamespaceManager
                );

            if (SharingXmlElement != null)
                m_Sharing = new Microsoft.CSA.FeedSync.FeedSyncLibrary.Sharing(SharingXmlElement, m_XmlNamespaceManager);

            this.PopulateItems();
        }

        public System.Xml.XmlElement ItemContainerXmlElement
        {
            get
            {
                return m_ItemContainerXmlElement;
            }
        }

        public System.Xml.XmlDocument XmlDocument
        {
            get
            {
                return m_XmlDocument;
            }
        }

        public System.Xml.XmlNamespaceManager XmlNamespaceManager
        {
            get
            {
                return m_XmlNamespaceManager;
            }
        }

        public Microsoft.CSA.FeedSync.FeedSyncLibrary.Sharing Sharing
        {
            get
            {
                return m_Sharing;
            }
        }

        public System.Collections.Generic.IList<T> Items
        {
            get
            {
                return m_FeedSyncItemSortedList.Values;
            }
        }

        public T GetItem(string i_ID)
        {
            if (System.String.IsNullOrEmpty(i_ID))
                throw new System.ArgumentException("i_ID must be non-null!");

            return m_FeedSyncItemSortedList[i_ID];
        }

        public bool HasItem(string i_ID)
        {
            if (System.String.IsNullOrEmpty(i_ID))
                throw new System.ArgumentException("i_ID must be non-null!");

            return m_FeedSyncItemSortedList.ContainsKey(i_ID);
        }

        public bool FindItem(string i_ID, out T o_Item)
        {
            if (System.String.IsNullOrEmpty(i_ID))
                throw new System.ArgumentException("i_ID must be non-null!");

            o_Item = null;

            if (!this.HasItem(i_ID))
                return false;

            o_Item = m_FeedSyncItemSortedList[i_ID];
            return true;
        }

        public void AddItem(T i_FeedSyncItem)
        {
            if (i_FeedSyncItem == null)
                throw new System.Exception("i_FeedSyncItem must be non-null!");

            if (this.HasItem(i_FeedSyncItem.Sync.ID))
                throw new System.ArgumentException("Specified item already exists!");

            if (i_FeedSyncItem.XmlElement.OwnerDocument != m_XmlDocument)
            {
                System.Xml.XmlElement ClonedXmlElement = (System.Xml.XmlElement)m_XmlDocument.ImportNode
                    (
                    i_FeedSyncItem.XmlElement,
                    true
                    );

                i_FeedSyncItem = new T();
                i_FeedSyncItem.Initialize
                    (
                    ClonedXmlElement, 
                    m_XmlNamespaceManager
                    );
            }

            m_ItemContainerXmlElement.AppendChild(i_FeedSyncItem.XmlElement);

            m_FeedSyncItemSortedList[i_FeedSyncItem.Sync.ID] = i_FeedSyncItem;
        }

        public void ReplaceItem(string i_ID, T i_FeedSyncItem)
        {
            if (System.String.IsNullOrEmpty(i_ID))
                throw new System.ArgumentException("i_ID must be non-null!");

            if (i_FeedSyncItem == null)
                throw new System.Exception("i_FeedSyncItem must be non-null!");

            T FeedSyncItem = null;

            if (!this.FindItem(i_ID, out FeedSyncItem))
                throw new System.ArgumentException("Specified item does not exist!");

            FeedSyncItem.XmlElement.ParentNode.RemoveChild(FeedSyncItem.XmlElement);

            if (i_FeedSyncItem.XmlElement.OwnerDocument != m_XmlDocument)
            {
                System.Xml.XmlElement ClonedXmlElement = (System.Xml.XmlElement)m_XmlDocument.ImportNode
                    (
                    i_FeedSyncItem.XmlElement,
                    true
                    );

                i_FeedSyncItem = new T();
                i_FeedSyncItem.Initialize
                    (
                    ClonedXmlElement, 
                    m_XmlNamespaceManager
                    );
            }

            m_ItemContainerXmlElement.AppendChild(i_FeedSyncItem.XmlElement);

            m_FeedSyncItemSortedList[i_ID] = i_FeedSyncItem;
        }

        public System.Collections.Generic.IList<T> GetMergeChangeList(Microsoft.CSA.FeedSync.FeedSyncLibrary.FeedSyncFeed<T> i_IncomingFeed)
        {
            System.Collections.Generic.IList<T> MergedFeedSyncItemList = Microsoft.CSA.FeedSync.FeedSyncLibrary.Sync.Merge
                (
                this.Items,
                i_IncomingFeed.Items
                );

            return MergedFeedSyncItemList;
        }

        public void Merge(Microsoft.CSA.FeedSync.FeedSyncLibrary.FeedSyncFeed<T> i_IncomingFeed)
        {
            System.Collections.Generic.IList<T> MergedFeedSyncItemList = this.GetMergeChangeList(i_IncomingFeed);

            //  Merge feeds
            foreach (T MergedFeedSyncItem in MergedFeedSyncItemList)
            {
                if (this.HasItem(MergedFeedSyncItem.Sync.ID))
                {
                    this.ReplaceItem
                        (
                        MergedFeedSyncItem.Sync.ID,
                        MergedFeedSyncItem
                        );
                }
                else
                {
                    this.AddItem(MergedFeedSyncItem);
                }
            }
        }

        protected virtual void PopulateItems()
        {
            //  Iterate items
            System.Xml.XmlNodeList FeedSyncItemXmlNodeList = m_ItemContainerXmlElement.SelectNodes
                (
                "*[sx:sync]",
                m_XmlNamespaceManager
                );

            foreach (System.Xml.XmlElement FeedSyncItemXmlElement in FeedSyncItemXmlNodeList)
            {
                T FeedSyncItem = new T();
                FeedSyncItem.Initialize
                    (
                    FeedSyncItemXmlElement,
                    m_XmlNamespaceManager
                    );

                m_FeedSyncItemSortedList[FeedSyncItem.Sync.ID] = FeedSyncItem;
            }
        }
    }
}
